#![allow(non_snake_case)]

use crate::{
    components::loading::Loading,
    report::{
        list_report::{get_rept_data, get_rept_options_map},
        report_header::ReportHeader,
    },
};
use charming::{Chart, ChartResize, Echarts, WasmRenderer};
use leptos::{html::Div, prelude::*};
use leptos_use::{use_element_size, UseElementSizeReturn};
use serde_json::Value;
use shq_common::prelude::report::ReportDefine;
use shq_web_controls::{ServerError, TableState};
use web_sys::js_sys;
#[component]
pub fn ChartReport(
    menu_id: u32,
    #[prop(into)] rept_name: StoredValue<String>,
    #[prop(into)] rept_define: Signal<ReportDefine>,
    #[prop(optional, into)] title: MaybeProp<String>,
    #[prop(optional, into)] params: MaybeProp<Vec<(String, String)>>,
    #[prop(optional, into)] extra_where: MaybeProp<String>,
) -> impl IntoView {
    let init_param_vals = Memo::new(move |_| {
        rept_define.with_untracked(|rd| {
            rd.params
                .iter()
                .map(|p| {
                    params.with_untracked(|ps| {
                        if let Some(ps) = ps {
                            if let Some(v) = ps.iter().find(|(k, _)| k == &p.name) {
                                (p.name.clone(), v.1.clone())
                            } else {
                                (p.name.clone(), p.default_value.to_string())
                            }
                        } else {
                            (p.name.clone(), p.default_value.to_string())
                        }
                    })
                })
                .collect::<Vec<_>>()
        })
    });
    let param_vals: RwSignal<Vec<(String, String)>> =
        RwSignal::new(init_param_vals.get_untracked());
    Effect::new(move |_| {
        params.get().map(|ps| {
            param_vals.update(|pvs| {
                for (pn, pv) in ps.iter() {
                    if let Some(v) = pvs.iter_mut().find(|(k, _)| k == pn) {
                        v.1 = pv.clone();
                    } else {
                        pvs.push((pn.clone(), pv.clone()));
                    }
                }
            });
        });
    });
    let inti_table_state = move || {
        rept_define.with_untracked(|rd| {
            let cols = rd.columns.as_slice();
            let excludeCols = rd.get_group_not_show_cols(0);
            TableState::from_column_cells(rept_name, cols, excludeCols.as_slice())
        })
    };
    let (table_state, _) = signal(inti_table_state());
    provide_context(table_state);
    let group_index = RwSignal::new(0_u8);
    let div_ref = NodeRef::<Div>::new();
    let options_map_res = Resource::new(
        move || init_param_vals.get(),
        move |param_vals| get_rept_options_map(menu_id, rept_name.get_value(), param_vals),
    );
    let json_vals = LocalResource::new(move || async move {
        let cols = rept_define.with_untracked(|rd| {
            rd.columns
                .iter()
                .filter_map(|c| c.as_leaf().map(|l| l.name.clone()))
                .collect::<Vec<_>>()
        });
        let where_causes = table_state.with_untracked(|ts| {
            let filters = ts.get_filters();
            if filters.is_empty() {
                None
            } else {
                Some(serde_json::to_string(&filters).unwrap())
            }
        });
        let chart_res = get_chart_rept_data(
            menu_id,
            rept_name.get_value(),
            None,
            param_vals.get(),
            where_causes,
            cols,
            extra_where.get(),
        )
        .await;
        chart_res
    });
    let UseElementSizeReturn { width, height } = use_element_size(div_ref);
    let chart_id = format!("__{}", rept_name.get_value());
    let chart_id_clone = chart_id.clone();
    on_cleanup( move || {
        let _ = js_sys::Reflect::delete_property(
            &js_sys::global(),
            &wasm_bindgen::JsValue::from_str(&chart_id_clone),
        );
    });
    Effect::new(move |prev: Option<(u32, u32)>| {
        let div_elem = div_ref.get();
        if div_elem.is_none() {
            return (0, 0);
        }
        let div_elem = div_elem.unwrap();
        let width = width.get() as u32;
        let mut height = height.get() as u32;
        if width == 0 || height == 0 {
            return (0, 0);
        }
        if height < 200 {
            height = 200;
        }
        if json_vals.get().is_none() {
            return (0, 0);
        }
        let echarts = js_sys::Reflect::get(
            &js_sys::global(),
            &wasm_bindgen::JsValue::from_str(&chart_id),
        );
        let echarts = match echarts {
            Ok(echarts) => {
                if echarts.is_undefined() || echarts.is_null() {
                    None
                } else {
                    Some(Echarts::from(echarts.clone()))
                }
            }
            Err(_) => None,
        };
        match json_vals.get().as_deref().unwrap() {
            Ok(chart) => {
                if let Some(echarts) = echarts {
                    WasmRenderer::update(&echarts, chart);
                    if !prev
                        .map(|(w, h)| w == width && h == height)
                        .unwrap_or(false)
                    {
                        WasmRenderer::resize_chart(
                            &echarts,
                            ChartResize::new(width, height, true, None),
                        );
                    }
                } else {
                    let renderer = WasmRenderer::new(width, height);
                    let echarts = renderer.render(&div_elem, chart).unwrap();
                    let _ = js_sys::Reflect::set(
                        &js_sys::global(),
                        &wasm_bindgen::JsValue::from_str(&chart_id),
                        &echarts,
                    );
                }
                (width, height)
            }
            Err(err) => {
                log::warn!("获取报表数据失败: {:?}", err.to_string());
                (0, 0)
            }
        }
    });
    let rept_header = move || {
        log::info!("ChartReport rept_header render");
        match options_map_res.get() {
            None => either_of::EitherOf3::A(view! { <Loading/> }),
            Some(mv_res) => match mv_res {
                Ok(mv) => either_of::EitherOf3::B(view! {
                    <ReportHeader title rept_define param_vals group_index options_map=mv/>
                }),
                Err(err) => either_of::EitherOf3::C(view! {
                    <div class="flex-1 bg-base-100 overflow-auto ">
                        <ServerError err_msg=err.to_string().into()/>
                    </div>
                }),
            },
        }
    };
    view! {
        <div class="flex-1 flex flex-col w-full h-full space-y-1 overflow-hidden bg-base-100">
            <Transition fallback=|| view! { <Loading/> }>
                {rept_header}
            </Transition>
            <div class="flex-1 w-full h-full overflow-x-hidden overflow-y-auto" node_ref=div_ref></div>
        </div>
    }
}
#[server(GetChartReptData, "/api/protect")]
pub async fn get_chart_rept_data(
    menu_id: u32,
    rept_name: String,
    title: Option<String>,
    #[server(default)] params: Vec<(String, String)>,
    where_causes: Option<String>,
    cols: Vec<String>,
    extra_where: Option<String>,
) -> Result<Chart, ServerFnError> {
    use shq_pas_backend::chart_series::get_chart_with_dataset;
    let result = get_rept_data(
        menu_id,
        rept_name.clone(),
        params,
        where_causes,
        None,
        cols,
        0,
        extra_where,
        1 as u32,
        10000 as u16,
    )
    .await;
    match result {
        Ok(datas) => {
            let row_datas = datas
                .data
                .into_iter()
                .filter_map(|v| match v {
                    Value::Array(arr) => Some(arr),
                    _ => None,
                })
                .collect::<Vec<_>>();
            let chart_res = get_chart_with_dataset(title, &rept_name, row_datas);
            chart_res.map_err(|e| {
                let err_msg = format!("get_chart_rept_data error:{}", e);
                ServerFnError::ServerError(err_msg)
            })
        }
        Err(e) => {
            log::error!("get_rept_data error:{}", e);
            Err(ServerFnError::ServerError(e.to_string()))
        }
    }
}
